package kycadminrepo

import (
	"context"
	"database/sql"
	"time"

	"code.bydev.io/cht/customer/kyc_admin_service/internal/constant"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/config"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/helper"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycadminrepo/o_kyc_action_state"
	"code.bydev.io/cht/customer/kyc_admin_service/internal/pkg/kycprovider/dto"
	"code.bydev.io/cht/customer/kyc_dbstructure.git/pkg/o_kyc_action_image"
	"code.bydev.io/frameworks/byone/core/logc"
	"emperror.dev/errors"
	"git.bybit.com/svc/go/pkg/bdal"
	"git.bybit.com/svc/go/pkg/bdal/bqb"
	"git.bybit.com/svc/go/pkg/bdebug"
	"git.bybit.com/svc/go/pkg/bstd"
	jsoniter "github.com/json-iterator/go"
)

// CreateActionImages create action images
func (r *repo) CreateActionImages(ctx context.Context, session *bdal.TX, images []*dto.KYCActionImage) error {
	var (
		pool    *bdal.DBPool
		records []*o_kyc_action_image.Record
		err     error
	)
	if session == nil {
		pool, err = r.kycDB.DBDefaultMasterNodePool(ctx)
		if err != nil {
			return err
		}
	}

	tNow := time.Now()
	for _, data := range images {
		actImg := o_kyc_action_image.New()
		actImg.IdempotentId = data.IdempotentID
		actImg.MemberId = data.MemberID
		actImg.RecordId = data.RecordID
		actImg.AccountId = data.AccountID
		actImg.Provider = int8(data.Provider)
		actImg.ImgId = data.ImgID
		actImg.ImgType = int8(data.ImgType)
		actImg.CreatedAt = tNow
		records = append(records, actImg)
	}

	if session != nil {
		return o_kyc_action_image.BatchInsertIgnore(ctx, session, records)
	} else {
		return o_kyc_action_image.BatchInsertIgnore(ctx, pool, records)
	}
}

// GetInitRecords get init records
func (r *repo) GetInitRecords(ctx context.Context, limit int) ([]*o_kyc_action_image.Record, error) {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}

	retryTimes := bqb.Field("retryTimes")
	selectSQL := o_kyc_action_image.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_image.FldImgStatus.EQ(wb)
		wb.LTE(o_kyc_action_image.FldRetryTimes, retryTimes.ParamPlaceholder())
	}).LimitC(limit).OrderByC(o_kyc_action_image.FldId, bqb.DESC).SQL()

	records, err := o_kyc_action_image.ScanRecords(selectSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_image.FldImgStatus.Param(): 0,
		retryTimes.Param():                      config.GetInt("common.retry_times_limit"),
	}))

	if err != nil {
		return nil, err
	}

	return records, nil
}

// GetInitSelfieRecords get init records
func (r *repo) GetInitSelfieRecords(ctx context.Context, limit int) ([]*o_kyc_action_image.Record, error) {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}

	retryTimes := bqb.Field("retryTimes")
	selectSQL := o_kyc_action_image.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_image.FldImgStatus.EQ(wb)
		o_kyc_action_image.FldImgType.EQ(wb)
		wb.LTE(o_kyc_action_image.FldRetryTimes, retryTimes.ParamPlaceholder())
	}).LimitC(limit).OrderByC(o_kyc_action_image.FldId, bqb.DESC).SQL()

	records, err := o_kyc_action_image.ScanRecords(selectSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_image.FldImgStatus.Param(): 0,
		o_kyc_action_image.FldImgType.Param():   constant.KYCImgTypeSelfie,
		retryTimes.Param():                      5,
	}))

	if err != nil {
		return nil, err
	}

	return records, nil
}

// GetInitRecordsByStartID get records by start id
func (r *repo) GetInitRecordsByStartID(ctx context.Context, startID int64, limit int) ([]*o_kyc_action_image.Record, error) {
	var records []*o_kyc_action_image.Record
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return records, err
	}

	createLte := bqb.Field("lteDate")
	selectSQL := o_kyc_action_image.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_image.FldImgStatus.EQ(wb)
		wb.GT(o_kyc_action_image.FldId, o_kyc_action_image.FldId.ParamPlaceholder())
		wb.LTE(o_kyc_action_image.FldCreatedAt, createLte.ParamPlaceholder())
	}).OrderByC(o_kyc_action_image.FldId, bqb.ASC).LimitC(limit).SQL()

	records, err = o_kyc_action_image.ScanRecords(selectSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_image.FldImgStatus.Param(): 0,
		o_kyc_action_image.FldId.Param():        startID,
		createLte.Param():                       time.Now().Add(-time.Hour * 24 * 14),
	}))

	if err != nil {
		return records, err
	}
	return records, nil
}

// action image complete
func (r *repo) ActionImageComplete(ctx context.Context, record *o_kyc_action_image.Record, doc *dto.Document) error {
	action, err := r.GetActionByIdempotentID(ctx, record.IdempotentId)
	if err != nil {
		logc.Errorw(ctx, "GetActionByIdempotentID error", logc.Field("action", action))
		return err
	}
	if action == nil {
		logc.Infow(ctx, "ActionImageComplete: action is nil", logc.Field("IdempotentId", record.IdempotentId))
		return nil
	}
	imgType := dto.ImgType(record.ImgType)

	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return err
	}
	session, txDispose, err := pool.BeginTX(ctx, nil)
	if err != nil {
		logc.Errorw(ctx, "pool.BeginTX error", logc.Field("err", err))
		return err
	}
	defer bstd.WithErrorHandler(txDispose, bdebug.Debug)()

	// 重试
	record.RetryTimes++
	// 图片不存在的时候，只更新 kyc_action_image
	if doc == nil {
		record.ImgStatus = 2
		_, err = record.Update(ctx, session)
		if err != nil {
			return err
		}
		if e := session.Commit(); e != nil {
			logc.Errorw(ctx, "session.Commit error", logc.Field("err", e))
			return e
		}
		return nil
	}

	// 更新 kyc_action_image
	record.ImgStatus = 1
	// 清空数据字段
	doc.Data = []byte("")
	imgBucket, _ := jsoniter.MarshalToString(doc)
	logc.Infow(ctx, "get imgBucket info", logc.Field("imgBucket", imgBucket))
	// 更新 kyc_action_image
	_, err = record.Update(ctx, session)
	if err != nil {
		logc.Errorw(ctx, "record.Update error", logc.Field("err", err))
		return err
	}
	if imgType == dto.ImgTypeLiveness {
		goto Commit
	} else if imgType == dto.ImgTypeFace {
		if action.FaceDetail == "" {
			logc.Infow(ctx, "faceDetail is empty", logc.Field("action", action))
			goto Commit
		}
		// 先解密
		faceJSONStr, err := helper.DeAES256CodeString(r.secHub, action.FaceDetail)
		if err != nil {
			return err
		}
		logc.Debugw(ctx, "helper.DeAES256CodeString", logc.Field("faceJSONStr", faceJSONStr))
		if faceJSONStr == "" {
			logc.Infow(ctx, "helper.DeAES256CodeString is empty", logc.Field("action", action))
			goto Commit
		}
		// 再反序列化
		faceDetail := new(dto.KYCAdminFaceDetail)
		err = jsoniter.UnmarshalFromString(faceJSONStr, faceDetail)
		if err != nil {
			return err
		}
		// 更新 faceDetail
		faceDetail.FaceImage = imgBucket
		faceDetailJSONStr, err := jsoniter.MarshalToString(faceDetail)
		if err != nil {
			return err
		}
		action.FaceDetail, err = helper.EnAES256CodeString(r.secHub, faceDetailJSONStr)
		if err != nil {
			return err
		}
		_, err = action.Update(ctx, session)
		if err != nil {
			return err
		}
		goto Commit
	} else {
		if action.DocDetail == "" {
			logc.Infow(ctx, "docDetail is empty", logc.Field("IdempotentId", action.IdempotentId))
			goto Commit
		}
		// 先解密
		docJSONStr, err := helper.DeAES256CodeString(r.secHub, action.DocDetail)
		if err != nil {
			return err
		}
		if docJSONStr == "" {
			logc.Infow(ctx, "docJSONStr is empty", logc.Field("IdempotentId", action.IdempotentId))
			goto Commit
		}
		// 再反序列化
		docDetail := new(dto.KYCAdminDocDetail)
		err = jsoniter.UnmarshalFromString(docJSONStr, docDetail)
		if err != nil {
			logc.Errorw(ctx, "jsoniter.UnmarshalFromString error", logc.Field("action", action))
			return err
		}
		// 更新 docDetail
		if imgType == dto.ImgTypeDocFront {
			docDetail.DocFront = imgBucket
		} else {
			docDetail.DocBack = imgBucket
		}
		docDetailJSONStr, err := jsoniter.MarshalToString(docDetail)
		if err != nil {
			logc.Errorw(ctx, "jsoniter.MarshalToString error", logc.Field("action", action))
			return err
		}
		action.DocDetail, err = helper.EnAES256CodeString(r.secHub, docDetailJSONStr)
		if err != nil {
			logc.Errorw(ctx, "helper.EnAES256CodeString error", logc.Field("err", err))
			return err
		}
		_, err = action.Update(ctx, session)
		if err != nil {
			return err
		}
		goto Commit
	}

Commit:
	if e := session.Commit(); e != nil {
		logc.Errorw(ctx, "session.Commit error", logc.Field("err", err))
		return e
	}
	return nil
}

// GetCompleteByImgID get complete by img id
func (r *repo) GetCompleteByImgID(ctx context.Context, memberID int64, provider int8, imgID string) (*o_kyc_action_image.Record, error) {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}

	selectSQL := o_kyc_action_image.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_image.FldImgId.EQ(wb)
		o_kyc_action_image.FldProvider.EQ(wb)
		o_kyc_action_image.FldMemberId.EQ(wb)
		o_kyc_action_image.FldImgStatus.EQ(wb)
	}).OrderByC(o_kyc_action_image.FldId, bqb.DESC).SQL()

	record, err := o_kyc_action_image.ScanRecordNil(selectSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_image.FldImgId.Param():     imgID,
		o_kyc_action_image.FldProvider.Param():  provider,
		o_kyc_action_image.FldMemberId.Param():  memberID,
		o_kyc_action_image.FldImgStatus.Param(): 1,
	}))
	if err != nil {
		return nil, err
	}
	return record, nil
}

func (r *repo) IsRecordDone(ctx context.Context, id int64) (bool, error) {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return false, err
	}
	record, err := o_kyc_action_image.Find(ctx, pool, id)
	switch {
	case err == nil:
		return record != nil && record.ImgStatus != 0, nil
	case errors.Is(err, sql.ErrNoRows):
		return true, nil
	default:
		return false, err
	}
}

func (r *repo) MarkImageNotFound(ctx context.Context, record *o_kyc_action_image.Record) error {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return err
	}

	// 重试
	record.RetryTimes++
	// 图片不存在的时候，只更新 kyc_action_image
	record.ImgStatus = 2
	_, err = record.Update(ctx, pool)
	if err != nil {
		return err
	}
	return nil
}

// GetRecordsByIdempotentID 根据idempotentID获取记录
func (r *repo) GetRecordsByIdempotentID(ctx context.Context, idempotentID string) ([]*o_kyc_action_image.Record, error) {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}

	selectSQL := o_kyc_action_image.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_image.FldIdempotentId.EQ(wb)
	}).SQL()

	records, err := o_kyc_action_image.ScanRecords(selectSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_image.FldIdempotentId.Param(): idempotentID,
	}))
	if err != nil {
		return nil, err
	}
	return records, nil
}

// GetLatestPOIComplete Get the user's latest record action which is complete
func (r *repo) GetLatestPOIComplete(ctx context.Context, memberID int64) (*o_kyc_action_state.Record, error) {
	var record *o_kyc_action_state.Record

	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}

	idpSQL := o_kyc_action_state.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_state.FldMemberId.EQ(wb)
		o_kyc_action_state.FldLevel.EQ(wb)
		o_kyc_action_state.FldDecision.EQ(wb)
	}).OrderByC(o_kyc_action_state.FldProviderCreateAt, bqb.DESC).LimitC(1).SQL()

	record, err = o_kyc_action_state.ScanRecordNil(idpSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_state.FldMemberId.Param(): memberID,
		o_kyc_action_state.FldLevel.Param():    int8(1),
		o_kyc_action_state.FldDecision.Param(): 1,
	}))
	if err != nil {
		return nil, err
	}

	return record, nil
}

func (r *repo) GetSelfieRecord(ctx context.Context, memberID int64) (*o_kyc_action_image.Record, error) {
	actionState, err := r.GetLatestPOIComplete(ctx, memberID)
	if err != nil {
		return nil, err
	}
	if actionState == nil {
		return nil, nil
	}

	var record *o_kyc_action_image.Record
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}

	idpSQL := o_kyc_action_image.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_image.FldMemberId.EQ(wb)
		o_kyc_action_image.FldImgType.EQ(wb)
		o_kyc_action_image.FldIdempotentId.EQ(wb)
		o_kyc_action_image.FldImgStatus.EQ(wb)
	}).OrderByC(o_kyc_action_image.FldId, bqb.DESC).LimitC(1).SQL()

	record, err = o_kyc_action_image.ScanRecordNil(idpSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_image.FldMemberId.Param():     memberID,
		o_kyc_action_image.FldImgType.Param():      constant.KYCImgTypeSelfie,
		o_kyc_action_image.FldIdempotentId.Param(): actionState.IdempotentId,
		o_kyc_action_image.FldImgStatus.Param():    0,
	}))
	if err != nil {
		return nil, err
	}

	return record, nil
}

// GetLatestMonthInitStatusRecords 获取最近一个月内初始化状态的记录
func (r *repo) GetLatestMonthInitStatusRecords(ctx context.Context, limit int) ([]*o_kyc_action_image.Record, error) {
	pool, err := r.kycDB.DBDefaultMasterNodePool(ctx)
	if err != nil {
		return nil, err
	}
	var records []*o_kyc_action_image.Record
	createLte := bqb.Field("lteDate")
	selectSQL := o_kyc_action_image.Select().WhereC(func(wb *bqb.WhereBuilder) {
		o_kyc_action_image.FldImgStatus.EQ(wb)
		wb.LTE(o_kyc_action_image.FldCreatedAt, createLte.ParamPlaceholder())
	}).LimitC(limit).SQL()

	records, err = o_kyc_action_image.ScanRecords(selectSQL.QueryArgs(ctx, pool, bqb.Args{
		o_kyc_action_image.FldImgStatus.Param(): 0,
		createLte.Param():                       time.Now().AddDate(0, -1, 0).Format("2006-01-02 15:04:05"),
	}))

	return records, err
}
